#include "cledMatrixRGB.h"
#include "cledMatrix.h"
#include "UGUI/ugui.h"
#include "LCD/lcd.h"
#include "cledMatrixRGB.h"
#include "Curve/hsv.h"
#include <math.h>
/*************************************************************************************************\
|        ColorWheel [9]                                                                              |
\*************************************************************************************************/
void ColorWheel_Init(void)       // 色轮
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 100;
    mParams.Slider2Val = 50;
    mParams.shape = 0;
    mParams.progress = 0.f;
}
#if 0
uint32_t ColorWheelEffect_GetColor(unsigned int x, unsigned int y, double cx, double cy, int reverse)
{
    float direction_mult = -1.0f;//direction == 0 ? 1.f : -1.f;
    float hue = (float)(mParams.progress + (int)(180 + direction_mult * (reverse ? atan2(y - cy, x - cx) : atan2(x - cx, y - cy)) * (180.0 / 3.14159)) % 360);
    hsv_t hsv = { 0, 0, 0 };
    hsv.hue = (int)hue;
    hsv.saturation = 255;
    hsv.value = 255;

    return (hsv2rgb(&hsv));
}
#else
static uint32_t ColorWheelEffect_GetColor(unsigned int x, unsigned int y, double cx, double cy, int reverse)
{
    //float direction_mult = -1.0f;//direction == 0 ? 1.f : -1.f;
    double angle;
    struct cledMatrixPixel piont;
    float hue;// = (float)(mParams.progress + (int)(180 + direction_mult * (reverse ? atan2(y - cy, x - cx) : atan2(x - cx, y - cy)) * (180.0 / 3.14159)) % 360);
    piont.pixel = cledMatrixAngleLimitGet((x - cx), (y - cy));
    angle = piont.angle/8.0f;
    //hue = (float)(mParams.progress + (int)(180 + direction_mult * (reverse ? atan2(y - cy, x - cx) : atan2(x - cx, y - cy)) * (180.0 / 3.14159)) % 360);
    hue = (float)(mParams.progress + (int)(360 + /*direction_mult **/(reverse ? angle : 90.0f-angle)) % 360);
    //hue = (float)(mParams.progress + (int)(180 + (atan2(x - cx, y - cy)) * (180.0 / 3.14159)) % 360);
    //hue = (float)(mParams.progress + (int)(360 - angle) % 360);
    hsv_t hsv = { 0, 0, 0 };
    hsv.hue = (int)hue;
    hsv.saturation = 255;
    hsv.value = 255;

    return (hsv2rgb(&hsv));
}
#endif
void ColorWheel(const uint32_t cols, const uint32_t rows)       // 色轮
{
    float cx_shift_mult = 0.5f;//cx_shift / 100.f;
    float cy_shift_mult = 0.5f;//cy_shift / 100.f;
    double cx = (cols - 1) * cx_shift_mult;
    double cy = (rows - 1) * cy_shift_mult;

    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
         for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = ColorWheelEffect_GetColor(col_id, row_id, cx, cy, 0);
            //int LedID = controller_zones[i]->controller->zones[controller_zones[i]->zone_idx].matrix_map->map[((row_id * cols) + col_id)];
            //int LedID = (row_id * cols) + col_id;
            //controller_zone->SetLED(start_idx + LedID, color, Brightness);
            ugui_pset(col_id, row_id, color);
        }
    }
    mParams.progress += (float) mParams.Speed / (float) mParams.FPS;
}

/*************************************************************************************************\
|        DoubleRotatingRainbow [9]                                                                              |
\*************************************************************************************************/
void DoubleRotatingRainbow_Init(void)       // 双旋转彩虹
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 50;
    mParams.Slider2Val = 20;
    mParams.shape = 0;
    mParams.progress = 0.f;
    mParams.frequency = 10;
}
static uint32_t DoubleRotatingRainbow_GetColor(float x, float y, float cx, float cy, int reverse)
{
//    double c = reverse? mParams.cos_reverse_time : mParams.cos_time;
//    double s = reverse? mParams.sin_reverse_time : mParams.sin_time;
    double c = reverse? cos(-mParams.time) : cos(mParams.time);
    double s = reverse? sin(-mParams.time) : sin(mParams.time);

    hsv_t hsv;

    hsv.hue = (int)(mParams.time * mParams.Slider2Val + 360 * (mParams.frequency * (y - cy) * c + (56 - fabs(x - cx)) * mParams.frequency * s) / 128.0) % 360;
    hsv.value = 255;
    hsv.saturation = 255;

    return (hsv2rgb(&hsv));
}
void DoubleRotatingRainbow(const uint32_t cols, const uint32_t rows)       // 双旋转彩虹
{
    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
           uint32_t color = DoubleRotatingRainbow_GetColor(col_id, row_id, (cols - 1) * 0.5, (rows - 1) * 0.5, 0);
           //int LedID = controller_zones[i]->controller->zones[controller_zones[i]->zone_idx].matrix_map->map[((row_id * cols) + col_id)];
           //int LedID = (row_id * cols) + col_id;
           //controller_zone->SetLED(start_idx + LedID, color, Brightness);
           ugui_pset(col_id, row_id, color);
        }
    }
    mParams.time += 0.01 * mParams.Speed / (float) mParams.FPS;
//    mParams.cos_time = cos(mParams.time);
//    mParams.sin_time = sin(mParams.time);
//    mParams.cos_reverse_time = cos(-mParams.time);
//    mParams.sin_reverse_time = sin(-mParams.time);
}

/*************************************************************************************************\
|        Hypnotoad [9]                                                                              |
\*************************************************************************************************/
static float animation_speed = 10.f;
static float color_rotation_speed = 10.f;
static unsigned int animation_direction = 0;
static unsigned int color_rotation_direction = 0;
static unsigned int spacing = 1;
static unsigned int thickness = 1;
//static unsigned int cx_shift = 50;
//static unsigned int cy_shift = 50;
void Hypnotoad_Init(void)                  // Hypnotoad
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 50;
    mParams.Slider2Val = 50;
    mParams.shape = 0;
    mParams.progress = 1000.f;
}
static uint32_t Hypnotoad_GetColor(unsigned int x, unsigned int y, float cx, float cy, int reverse)
{
    float animation_mult = 0.01 * (reverse ? -1.0 : 1.0) * animation_speed * (animation_direction == 0 ? 1 : -1);
    float color_mult = 0.01 * (reverse ? -1.0 : 1.0) * color_rotation_speed * (color_rotation_direction == 0 ? -1 : 1);

    double angle    = atan2(y - cy, x - cx) * 180 / 3.14159265359;
    double distance = sqrt(pow(cx - x, 2) + pow(cy - y, 2));
    float  value    = cos(animation_mult * distance / (0.1 * (float) spacing)  + mParams.progress);

    hsv_t hsv;
    hsv.value = pow((value + 1) * 0.5, (11 - thickness)) * 255;
    hsv.hue = abs((int)(angle + distance + mParams.progress * color_mult * color_rotation_speed) % 360);
    hsv.saturation = 255;

    return (hsv2rgb(&hsv));
}
void Hypnotoad(const uint32_t cols, const uint32_t rows)                  // Hypnotoad
{
    float cx_shift_mult = 0.5f;//cx_shift / 100.f;
    float cy_shift_mult = 0.5f;//cy_shift / 100.f;
    unsigned int width = cols;
    unsigned int height = rows;
    //unsigned int * map = controller_zone->map();

    float cx = (width-1) * cx_shift_mult;
    float cy = (height-1) * cy_shift_mult;

    int reverse=1;
    for(unsigned int row_id = 0; row_id < height; row_id++)
    {
        for(unsigned int col_id = 0; col_id <  width; col_id++)
        {
            uint32_t color = Hypnotoad_GetColor(col_id, row_id, cx, cy, reverse);

            //unsigned int led_num = map[h * width + w];
            //unsigned int led_num = h * width + w;
            //controller_zone->SetLED(start_idx + led_num, ColorUtils::fromQColor(color), Brightness);
            ugui_pset(col_id, row_id, color);
        }
    }
    mParams.progress +=  0.1 * (float) mParams.Speed / (float) mParams.FPS;
}

/*************************************************************************************************\
|        NoiseMap [9]                                                                              |
\*************************************************************************************************/
struct SimplexNoise {
    // Parameters of Fractional Brownian Motion (fBm) : sum of N "octaves" of noise
    float mFrequency;   ///< Frequency ("width") of the first octave of noise (default to 1.0)
    float mAmplitude;   ///< Amplitude ("height") of the first octave of noise (default to 1.0)
    float mLacunarity;  ///< Lacunarity specifies the frequency multiplier between successive octaves (default to 2.0).
    float mPersistence; ///< Persistence is the loss of amplitude between successive octaves (usually 1/lacunarity)
};
static float frequency = 0.12;
static float amplitude = 3.9;
static float lacunarity = 0.75;
static float persistence = 0.5;
static int octaves = 2;
static int motion = 0;
static int motion_speed = 0;
static struct SimplexNoise Noise;
void NoiseMap_Init(void)                   // 噪声地图
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 50;
    mParams.Slider2Val = 50;
    mParams.shape = 0;
    mParams.progress = 0.f;
    //ResetNoise();
    Noise.mFrequency=(frequency);
    Noise.mAmplitude=(amplitude);
    Noise.mLacunarity=(lacunarity);
    Noise.mPersistence=(persistence);
    //ResetColors();
}
/**
 * Permutation table. This is just a random jumble of all numbers 0-255.
 *
 * This produce a repeatable pattern of 256, but Ken Perlin stated
 * that it is not a problem for graphic texture as the noise features disappear
 * at a distance far enough to be able to see a repeatable pattern of 256.
 *
 * This needs to be exactly the same for all instances on all platforms,
 * so it's easiest to just keep it as static explicit data.
 * This also removes the need for any initialisation of this class.
 *
 * Note that making this an uint32_t[] instead of a uint8_t[] might make the
 * code run faster on platforms with a high penalty for unaligned single
 * byte addressing. Intel x86 is generally single-byte-friendly, but
 * some other CPUs are faster with 4-aligned reads.
 * However, a char[] is smaller, which avoids cache trashing, and that
 * is probably the most important aspect on most architectures.
 * This array is accessed a *lot* by the noise functions.
 * A vector-valued noise over 3D accesses it 96 times, and a
 * float-valued 4D noise 64 times. We want this to fit in the cache!
 */
static const uint8_t perm[256] = {
    151, 160, 137, 91, 90, 15,
    131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23,
    190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33,
    88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166,
    77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244,
    102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196,
    135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123,
    5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42,
    223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
    129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228,
    251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107,
    49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254,
    138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
};
/**
 * Computes the largest integer value not greater than the float one
 *
 * This method is faster than using (int32_t)std::floor(fp).
 *
 * I measured it to be approximately twice as fast:
 *  float:  ~18.4ns instead of ~39.6ns on an AMD APU),
 *  double: ~20.6ns instead of ~36.6ns on an AMD APU),
 * Reference: http://www.codeproject.com/Tips/700780/Fast-floor-ceiling-functions
 *
 * @param[in] fp    float input value
 *
 * @return largest integer value not greater than fp
 */
static inline int32_t fastfloor(float fp) {
    int32_t i = (int32_t)(fp);
    return (fp < i) ? (i - 1) : (i);
}
/**
 * Helper function to hash an integer using the above permutation table
 *
 *  This inline function costs around 1ns, and is called N+1 times for a noise of N dimension.
 *
 *  Using a real hash function would be better to improve the "repeatability of 256" of the above permutation table,
 * but fast integer Hash functions uses more time and have bad random properties.
 *
 * @param[in] i Integer value to hash
 *
 * @return 8-bits hashed value
 */
static inline uint8_t hash(int32_t i) {
    return perm[(uint8_t)(i)];
}
/**
 * Helper functions to compute gradients-dot-residual vectors (3D)
 *
 * @param[in] hash  hash value
 * @param[in] x     x coord of the distance to the corner
 * @param[in] y     y coord of the distance to the corner
 * @param[in] z     z coord of the distance to the corner
 *
 * @return gradient value
 */
static float grad(int32_t hash, float x, float y, float z) {
    int h = hash & 15;     // Convert low 4 bits of hash code into 12 simple
    float u = h < 8 ? x : y; // gradient directions, and compute dot product.
    float v = h < 4 ? y : h == 12 || h == 14 ? x : z; // Fix repeats at h = 12 to 15
    return ((h & 1) ? -u : u) + ((h & 2) ? -v : v);
}
/**
 * 3D Perlin simplex noise
 *
 * @param[in] x float coordinate
 * @param[in] y float coordinate
 * @param[in] z float coordinate
 *
 * @return Noise value in the range[-1; 1], value of 0 on all integer coordinates.
 */
static float SimplexNoise_noise(float x, float y, float z)
{
    float n0, n1, n2, n3; // Noise contributions from the four corners

    // Skewing/Unskewing factors for 3D
    static const float F3 = 1.0f / 3.0f;
    static const float G3 = 1.0f / 6.0f;

    // Skew the input space to determine which simplex cell we're in
    float s = (x + y + z) * F3; // Very nice and simple skew factor for 3D
    int i = fastfloor(x + s);
    int j = fastfloor(y + s);
    int k = fastfloor(z + s);
    float t = (i + j + k) * G3;
    float X0 = i - t; // Unskew the cell origin back to (x,y,z) space
    float Y0 = j - t;
    float Z0 = k - t;
    float x0 = x - X0; // The x,y,z distances from the cell origin
    float y0 = y - Y0;
    float z0 = z - Z0;

    // For the 3D case, the simplex shape is a slightly irregular tetrahedron.
    // Determine which simplex we are in.
    int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
    int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
    if (x0 >= y0) {
        if (y0 >= z0) {
            i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; // X Y Z order
        } else if (x0 >= z0) {
            i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; // X Z Y order
        } else {
            i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; // Z X Y order
        }
    } else { // x0<y0
        if (y0 < z0) {
            i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1; // Z Y X order
        } else if (x0 < z0) {
            i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1; // Y Z X order
        } else {
            i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0; // Y X Z order
        }
    }

    // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
    // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
    // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
    // c = 1/6.
    float x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
    float y1 = y0 - j1 + G3;
    float z1 = z0 - k1 + G3;
    float x2 = x0 - i2 + 2.0f * G3; // Offsets for third corner in (x,y,z) coords
    float y2 = y0 - j2 + 2.0f * G3;
    float z2 = z0 - k2 + 2.0f * G3;
    float x3 = x0 - 1.0f + 3.0f * G3; // Offsets for last corner in (x,y,z) coords
    float y3 = y0 - 1.0f + 3.0f * G3;
    float z3 = z0 - 1.0f + 3.0f * G3;

    // Work out the hashed gradient indices of the four simplex corners
    int gi0 = hash(i + hash(j + hash(k)));
    int gi1 = hash(i + i1 + hash(j + j1 + hash(k + k1)));
    int gi2 = hash(i + i2 + hash(j + j2 + hash(k + k2)));
    int gi3 = hash(i + 1 + hash(j + 1 + hash(k + 1)));

    // Calculate the contribution from the four corners
    float t0 = 0.6f - x0*x0 - y0*y0 - z0*z0;
    if (t0 < 0) {
        n0 = 0.0;
    } else {
        t0 *= t0;
        n0 = t0 * t0 * grad(gi0, x0, y0, z0);
    }
    float t1 = 0.6f - x1*x1 - y1*y1 - z1*z1;
    if (t1 < 0) {
        n1 = 0.0;
    } else {
        t1 *= t1;
        n1 = t1 * t1 * grad(gi1, x1, y1, z1);
    }
    float t2 = 0.6f - x2*x2 - y2*y2 - z2*z2;
    if (t2 < 0) {
        n2 = 0.0;
    } else {
        t2 *= t2;
        n2 = t2 * t2 * grad(gi2, x2, y2, z2);
    }
    float t3 = 0.6f - x3*x3 - y3*y3 - z3*z3;
    if (t3 < 0) {
        n3 = 0.0;
    } else {
        t3 *= t3;
        n3 = t3 * t3 * grad(gi3, x3, y3, z3);
    }
    // Add contributions from each corner to get the final noise value.
    // The result is scaled to stay just inside [-1,1]
    return 32.0f*(n0 + n1 + n2 + n3);
}
/**
 * Fractal/Fractional Brownian Motion (fBm) summation of 3D Perlin Simplex noise
 *
 * @param[in] octaves   number of fraction of noise to sum
 * @param[in] x         x float coordinate
 * @param[in] y         y float coordinate
 * @param[in] z         z float coordinate
 *
 * @return Noise value in the range[-1; 1], value of 0 on all integer coordinates.
 */
static float SimplexNoise_fractal(struct SimplexNoise* const noise, size_t octaves, float x, float y, float z)
{
    float output = 0.f;
    float denom  = 0.f;
    float frequency = noise->mFrequency;
    float amplitude = noise->mAmplitude;

    for (size_t i = 0; i < octaves; i++) {
        output += (amplitude * SimplexNoise_noise(x * frequency, y * frequency, z * frequency));
        denom += amplitude;

        frequency *= noise->mLacunarity;
        amplitude *= noise->mPersistence;
    }

    return (output / denom);
}

static uint32_t NoiseMap_GetColor(unsigned int x, unsigned int y)
{
    float x_shift = 0;
    float y_shift = 0;

    switch (motion) {

    case 0: y_shift = motion_speed * mParams.progress; break;
    case 1: y_shift = motion_speed * -mParams.progress;  break;
    case 2: x_shift = motion_speed * mParams.progress;  break;
    case 3: x_shift = motion_speed * -mParams.progress;  break;

    default: break;
    }


    float value = SimplexNoise_fractal(&Noise, octaves, x + x_shift, y + y_shift, mParams.progress);
    float frac = (1+value)*0.5;

    int color_choice = 0;//ui->colors_choice->currentIndex();
    hsv_t hsv;

    switch (color_choice)
    {
    case 0:
        hsv.hue = 360 * frac;
        hsv.saturation = 255;
        hsv.value = 255 ;
        return (hsv2rgb(&hsv));

    case 1:
        hsv.hue = 360 - 360 * frac;
        hsv.saturation = 255;
        hsv.value = 255 ;
        return (hsv2rgb(&hsv));

//    case 2:
//        int color_x = (1 - frac) * 100;
//        QColor c = image.pixelColor(color_x, 0);
//        return ColorUtils::fromQColor(c);
    }

    //return ColorUtils::OFF();
    return 0x00;
}
void NoiseMap(const uint32_t cols, const uint32_t rows)                   // 噪声地图
{
    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = NoiseMap_GetColor(col_id, row_id);
            //int LedID = controller_zone->map[((row_id * cols) + col_id)];
            //int LedID = (row_id * cols) + col_id;
            //controller_zone->SetLED(start_idx + LedID, color, Brightness);
            ugui_pset(col_id, row_id, color);
        }
    }
    mParams.progress +=  0.1 * (float) mParams.Speed / (float) mParams.FPS;
}

/*************************************************************************************************\
|        Rainbow [9]                                                                              |
\*************************************************************************************************/
static uint32_t RadialRainbow_GetColor(unsigned int x, unsigned int y, double cx, double cy, int reverse)
{
    hsv_t hsv;
    float width = mParams.Slider2Val * 0.5f;
    double distance, distance2;
    struct cledMatrixPixel piont;

    if(mParams.shape == 0)
    {
        //distance = sqrt(pow(cx - x, 2) + pow(cy - y, 2));
        piont.pixel = cledMatrixAngleLimitGet((x - cx), (y - cy));
        distance = piont.radius;
    }
    else if (mParams.shape == 1)
    {
        //distance = std::max<float>(fabs(cy - y), fabs(cx - x));
        distance = fabs(cy - y);
        distance2 = fabs(cx - x);
        if(distance<distance2) distance=distance2;
    }
    else
    {
        return 0x00;//ColorUtils::OFF();
    }

    hsv.hue = distance * width + (reverse ? mParams.progress:-mParams.progress);
    hsv.hue %= 360;

    hsv.saturation = 255;
    hsv.value = 255;

    return (hsv2rgb(&hsv));
}
void RadialRainbow_Init(void)       // 径向彩虹
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 100;
    mParams.Slider2Val = 50;
    mParams.shape = 0;
    mParams.progress = 0.f;
}
void RadialRainbow(const uint32_t cols, const uint32_t rows)       // 径向彩虹
{
    float cx_shift_mult = 0.5f;//cx_shift / 100.f;
    float cy_shift_mult = 0.5f;//cy_shift / 100.f;
    double cx = (cols - 1) * cx_shift_mult;
    double cy = (rows - 1) * cy_shift_mult;

    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = RadialRainbow_GetColor(col_id, row_id, cx, cy, 0);
            //int LedID = controller_zones[i]->controller->zones[controller_zones[i]->zone_idx].matrix_map->map[((row_id * cols) + col_id)];
            //int LedID = (row_id * cols) + col_id;
            //controller_zone->SetLED(start_idx + LedID, color, Brightness);
            ugui_pset(col_id, row_id, color);
        }
    }
    mParams.progress += (float) mParams.Speed / (float) mParams.FPS;
}

/*************************************************************************************************\
|        RainbowWave [9]                                                                              |
\*************************************************************************************************/
void RainbowWave_Init(void)       // 彩虹波
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 40;
    mParams.Slider2Val = 10;
    mParams.shape = 0;
    mParams.progress = 0.f;
}
void RainbowWave(const uint32_t cols, const uint32_t rows)       // 彩虹波
{
    int Width = mParams.Slider2Val;

    hsv_t HSVVal;
    HSVVal.saturation = 255;
    HSVVal.value      = 255;
    //int ColumnCount = cols;
    //int RowCount = rows;

    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        int HUE;
        int RVRS = 1;

        if (RVRS)
        {
            HUE = ((mParams.progress + (int)( (cols - 1) - col_id)) * Width);
        }
        else
        {
            HUE = ((mParams.progress + (int)col_id) * Width);
        }

        HSVVal.hue = HUE;

        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            //int LedID = controller_zone->controller->zones[controller_zone->zone_idx].matrix_map->map[((RowID * ColumnCount) + ColumnID)];
            //int LedID = (RowID * ColumnCount) + ColumnID;
            //controller_zone->SetLED(start_idx + LedID, RGBColor(hsv2rgb(&HSVVal)), Brightness);
            ugui_pset(col_id, row_id, hsv2rgb(&HSVVal));
        }
    }
    if (mParams.progress < 360)
    {
        mParams.progress += (((float)mParams.Speed) / ((float)mParams.FPS));
    }
    else if (mParams.progress >= 360)
    {
        mParams.progress = 0;
    }
}

/*************************************************************************************************\
|        RotatingRainbow [9]                                                                              |
\*************************************************************************************************/
void RotatingRainbow_Init(void)       // 旋转的彩虹
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 20;
    mParams.Slider2Val = 30;
    mParams.shape = 0;
    mParams.progress = 0.f;
}
static uint32_t RotatingRainbow_GetColor(float x, float y, float cx, float cy, int reverse)
{
//    double c = reverse? cos_reverse_time : cos_time;
//    double s = reverse? sin_reverse_time : sin_time;
    double c = reverse? cos(-mParams.time) : cos(mParams.time);
    double s = reverse? sin(-mParams.time) : sin(mParams.time);

    hsv_t hsv;

    hsv.hue = (int)(mParams.time * mParams.Slider2Val + 360 * ((y - cy) * 2 * c + (x - cx) * 2 * s) / 128) % 360;
    hsv.value = 255;
    hsv.saturation = 255;

    return (hsv2rgb(&hsv));
}

void RotatingRainbow(const uint32_t cols, const uint32_t rows)       // 旋转的彩虹
{
    float cx = (cols - 1) * 0.5;
    float cy = (rows - 1) * 0.5;

    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
           uint32_t color = RotatingRainbow_GetColor(col_id, row_id, cx, cy, 0);
           //int LedID = controller_zones[i]->controller->zones[controller_zones[i]->zone_idx].matrix_map->map[((row_id * cols) + col_id)];
           //int LedID = (row_id * cols) + col_id;
           //controller_zone->SetLED(start_idx + LedID, color, Brightness);
           ugui_pset(col_id, row_id, color);
        }
    }
    mParams.time += 0.1 * mParams.Speed / (float) mParams.FPS;

//    cos_time = cos(mParams.time);
//    sin_time = sin(mParams.time);

//    cos_reverse_time = cos(-mParams.time);
//    sin_reverse_time = sin(-mParams.time);
}

void SpectrumCycling_Init(void)       // 光谱骑自行车
{
    mParams.time = 1000.0;
    mParams.FPS = 60;
    mParams.Speed = 50;
    mParams.Slider2Val = 30;
    mParams.shape = 0;
    mParams.progress = 0.f;
}
void SpectrumCycling(const uint32_t cols, const uint32_t rows)       // 光谱骑自行车
{
    hsv_t HSVVal;

    HSVVal.value = 255;
    HSVVal.saturation = 255;
    HSVVal.hue = ((int)mParams.progress) % 360;

    uint32_t color = (hsv2rgb(&HSVVal));

    //for(ControllerZone* controller_zone: controller_zones)
//    {
//        controller_zone->SetAllZoneLEDs(color, Brightness);
//    }
    for (uint32_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
           ugui_pset(col_id, row_id, color);
        }
    }

    mParams.progress += (float) mParams.Speed / (float) mParams.FPS;
}

